{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Collecting pygame\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/c2/c9/a41e2dc929529c36d1d1a6cf989f7d69225584d8e1b1e85c70d4b4bcb230/pygame-2.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (13.9 MB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.9/13.9 MB\u001b[0m \u001b[31m1.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n",
      "\u001b[?25hInstalling collected packages: pygame\n",
      "Successfully installed pygame-2.5.2\n",
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.2.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install pygame"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Requirement already satisfied: pip in /data/projects/py-life/.venv/lib/python3.12/site-packages (23.2.1)\n",
      "Collecting pip\n",
      "  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl (2.1 MB)\n",
      "\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.1/2.1 MB\u001b[0m \u001b[31m1.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n",
      "\u001b[?25hInstalling collected packages: pip\n",
      "  Attempting uninstall: pip\n",
      "    Found existing installation: pip 23.2.1\n",
      "    Uninstalling pip-23.2.1:\n",
      "      Successfully uninstalled pip-23.2.1\n",
      "Successfully installed pip-24.0\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install --upgrade pip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pygame\n",
    "import random\n",
    "\n",
    "# 初始化 pygame\n",
    "pygame.init()\n",
    "\n",
    "# 设置窗口大小\n",
    "window_width = 800\n",
    "window_height = 600\n",
    "window = pygame.display.set_mode((window_width, window_height))\n",
    "\n",
    "# 设置颜色\n",
    "white = (255, 255, 255)\n",
    "blue = (0, 0, 255)\n",
    "green = (0, 255, 0)\n",
    "red = (255, 0, 0)\n",
    "\n",
    "\n",
    "# 生命体类\n",
    "class SimpleLife:\n",
    "    def __init__(self, x, y):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "        self.energy = 100\n",
    "\n",
    "    def live(self, environment):\n",
    "        self.energy -= 1  # 模拟生命活动消耗能量\n",
    "        # 这里可以添加生命体与环境的交互逻辑\n",
    "\n",
    "\n",
    "# 环境类\n",
    "class Environment:\n",
    "    def __init__(self, width, height):\n",
    "        self.width = width\n",
    "        self.height = height\n",
    "        self.food_sources = self.generate_food_sources(10)\n",
    "        self.dangers = self.generate_dangers(5)\n",
    "\n",
    "    def generate_food_sources(self, count):\n",
    "        return [\n",
    "            {\"x\": random.randint(0, self.width), \"y\": random.randint(0, self.height)}\n",
    "            for _ in range(count)\n",
    "        ]\n",
    "\n",
    "    def generate_dangers(self, count):\n",
    "        return [\n",
    "            {\"x\": random.randint(0, self.width), \"y\": random.randint(0, self.height)}\n",
    "            for _ in range(count)\n",
    "        ]\n",
    "\n",
    "\n",
    "# 创建环境\n",
    "environment = Environment(window_width, window_height)\n",
    "\n",
    "# 创建生命体\n",
    "life = SimpleLife(random.randint(0, window_width), random.randint(0, window_height))\n",
    "\n",
    "# 游戏主循环\n",
    "running = True\n",
    "while running:\n",
    "    for event in pygame.event.get():\n",
    "        if event.type == pygame.QUIT:\n",
    "            running = False\n",
    "\n",
    "    # 更新生命体的状态\n",
    "    life.live(environment)\n",
    "\n",
    "    # 绘制生命体\n",
    "    pygame.draw.circle(window, blue, (life.x, life.y), 10)\n",
    "\n",
    "    # 绘制食物\n",
    "    for food in environment.food_sources:\n",
    "        pygame.draw.circle(window, green, (food[\"x\"], food[\"y\"]), 5)\n",
    "\n",
    "    # 绘制危险\n",
    "    for danger in environment.dangers:\n",
    "        pygame.draw.circle(window, red, (danger[\"x\"], danger[\"y\"]), 5)\n",
    "\n",
    "    # 更新屏幕\n",
    "    pygame.display.flip()\n",
    "\n",
    "# 退出游戏\n",
    "pygame.quit()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (565.0, 165.0)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (581.0, 194.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (581, 194)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (713.0, 225.0)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (785.0, 209.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (785, 209)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (585.0, 8.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (585, 8)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (426.0, 85.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (426, 85)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (403.0, 7.0)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (313.0, 70.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (313, 70)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (241, 432)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (234.0, 381.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (234, 381)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (178.0, 301.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (178, 301)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (190.0, 208.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (190, 208)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (240.0, 222.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (340.0, 250.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (231.0, 337.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (322.0, 480.0)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (233.0, 525.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (233, 525)\n",
      "food is closer, move to it first\n",
      "move to food\n",
      "draw life:  (47.0, 371.0)\n",
      "food is closer, move to it first\n",
      "draw life:  (47, 371)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (635, 35)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (284, 73)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (394, 119)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (339.0, 75.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (203, 307)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (266.0, 420.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (512, 594)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (592, 324)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (735.0, 485.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (504, 248)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (559.0, 333.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (669.0, 503.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (156, 138)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (172.0, 82.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (378, 159)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (307.0, 155.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (165.0, 147.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (190.0, 100.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "draw life:  (240.0, 6.0)\n",
      "danger is closer, move away from it first\n",
      "move away from danger\n",
      "!!! life moved out of boundary, refresh to center\n",
      "draw life:  (312, 234)\n"
     ]
    }
   ],
   "source": [
    "import pygame\n",
    "import random\n",
    "import math\n",
    "\n",
    "# 初始化 pygame\n",
    "pygame.init()\n",
    "\n",
    "# 设置窗口大小\n",
    "window_width = 800\n",
    "window_height = 600\n",
    "window = pygame.display.set_mode((window_width, window_height))\n",
    "\n",
    "# 设置颜色\n",
    "white = (255, 255, 255)\n",
    "blue = (0, 0, 255)\n",
    "green = (0, 255, 0)\n",
    "red = (255, 0, 0)\n",
    "\n",
    "\n",
    "class Environment:\n",
    "    def __init__(self, width, height):\n",
    "        self.width = width\n",
    "        self.height = height\n",
    "        self.food_sources = self.generate_food_sources(10)  # 生成食物源\n",
    "        self.dangers = self.generate_dangers(2)  # 生成危险\n",
    "\n",
    "    def generate_food_sources(self, count):\n",
    "        return [\n",
    "            {\n",
    "                \"x\": random.randint(0, self.width),\n",
    "                \"y\": random.randint(0, self.height),\n",
    "                \"energy\": random.randint(5, 20),\n",
    "            }\n",
    "            for _ in range(count)\n",
    "        ]\n",
    "\n",
    "    def generate_dangers(self, count):\n",
    "        return [\n",
    "            {\n",
    "                \"x\": random.randint(0, self.width),\n",
    "                \"y\": random.randint(0, self.height),\n",
    "                \"damage\": random.randint(10, 30),\n",
    "            }\n",
    "            for _ in range(count)\n",
    "        ]\n",
    "\n",
    "    def find_closest_food(self, x, y):\n",
    "        closest_food = None\n",
    "        min_distance = float(\"inf\")\n",
    "        for food in self.food_sources:\n",
    "            distance = ((food[\"x\"] - x) ** 2 + (food[\"y\"] - y) ** 2) ** 0.5\n",
    "            if distance < min_distance:\n",
    "                min_distance = distance\n",
    "                closest_food = food\n",
    "        return closest_food\n",
    "\n",
    "    def find_closest_danger(self, x, y):\n",
    "        closest_danger = None\n",
    "        min_distance = float(\"inf\")\n",
    "        for danger in self.dangers:\n",
    "            distance = ((danger[\"x\"] - x) ** 2 + (danger[\"y\"] - y) ** 2) ** 0.5\n",
    "            if distance < min_distance:\n",
    "                min_distance = distance\n",
    "                closest_danger = danger\n",
    "        return closest_danger\n",
    "\n",
    "\n",
    "class SimpleLife:\n",
    "    def __init__(self, energy, genes=None, x=0, y=0):\n",
    "        self.energy = energy\n",
    "        self.age = 0\n",
    "        self.genes = genes or {\"speed\": 1.0, \"sense\": 3.0}\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def live(self, environment):\n",
    "        self.age += 1\n",
    "        self.energy -= 1  # 模拟生命活动消耗能量\n",
    "\n",
    "        # 寻找食物\n",
    "        food = environment.find_closest_food(self.x, self.y)\n",
    "        if food:\n",
    "            distance = ((food[\"x\"] - self.x) ** 2 + (food[\"y\"] - self.y) ** 2) ** 0.5\n",
    "            # print(distance)\n",
    "            # print(self.genes)\n",
    "        else:\n",
    "            distance = float(\"inf\")\n",
    "        # 避免危险\n",
    "        danger = environment.find_closest_danger(self.x, self.y)\n",
    "        if danger:\n",
    "            distance_danger = (\n",
    "                (danger[\"x\"] - self.x) ** 2 + (danger[\"y\"] - self.y) ** 2\n",
    "            ) ** 0.5\n",
    "            if distance_danger < distance:\n",
    "                print(\"danger is closer, move away from it first\")\n",
    "                distance = distance_danger\n",
    "                if distance < self.genes[\"sense\"]:\n",
    "                    self.energy -= danger[\"damage\"]\n",
    "                    environment.dangers.remove(danger)\n",
    "                # 远离危险的方向移动，远离危险之地\n",
    "                print(\"move away from danger\")\n",
    "                self.move_away(danger[\"x\"], danger[\"y\"])\n",
    "                # check environment boundary\n",
    "                if (\n",
    "                    self.x >= environment.width\n",
    "                    or self.y >= environment.height\n",
    "                    or self.x < 0\n",
    "                    or self.y < 0\n",
    "                ):\n",
    "                    self.refreshPosition(environment)\n",
    "            else:\n",
    "                print(\"food is closer, move to it first\")\n",
    "                if distance < self.genes[\"sense\"]:\n",
    "                    self.eat(food[\"energy\"])\n",
    "                    environment.food_sources.remove(food)\n",
    "                    # 移动到食物的位置\n",
    "                    self.x, self.y = food[\"x\"], food[\"y\"]\n",
    "                else:\n",
    "                    # 向食物的方向移动\n",
    "                    print(\"move to food\")\n",
    "                    self.move_towards(food[\"x\"], food[\"y\"])\n",
    "                    # check environment boundary\n",
    "                    if (\n",
    "                        self.x >= environment.width\n",
    "                        or self.y >= environment.height\n",
    "                        or self.x < 0\n",
    "                        or self.y < 0\n",
    "                    ):\n",
    "                        self.refreshPosition(environment)\n",
    "\n",
    "    def refreshPosition(self, environment):\n",
    "        print(\"!!! life moved out of boundary, refresh to center\")\n",
    "        self.x = random.randint(0, window_width)\n",
    "        self.y = random.randint(0, window_height)\n",
    "\n",
    "    def move_towards(self, target_x, target_y):\n",
    "        # 向目标移动\n",
    "        if (\n",
    "            math.sqrt((self.x - target_x) ** 2 + (self.y - target_y) ** 2)\n",
    "            <= self.genes[\"speed\"]\n",
    "        ):  # 如果目标在速度范围内，直接移动到目标位置进行操作\n",
    "            self.x = target_x\n",
    "            self.y = target_y\n",
    "\n",
    "        # print(f\"move to {target_x, target_y}\")\n",
    "        self.x += (target_x - self.x) * self.genes[\"speed\"]\n",
    "        self.y += (target_y - self.y) * self.genes[\"speed\"]\n",
    "\n",
    "    def move_away(self, target_x, target_y):\n",
    "        # 远离目标移动\n",
    "        # print(f\"move away from {target_x, target_y}\")\n",
    "        self.x -= (target_x - self.x) * self.genes[\"speed\"]\n",
    "        self.y -= (target_y - self.y) * self.genes[\"speed\"]\n",
    "\n",
    "    def eat(self, food_energy):\n",
    "        self.energy += food_energy\n",
    "\n",
    "    def reproduce(self):\n",
    "        if self.energy > 20:  # 假设能量超过20时可以繁殖\n",
    "            new_energy = self.energy // 2\n",
    "            self.energy = new_energy\n",
    "            new_genes = {\n",
    "                gene: value * (1 + random.uniform(-0.1, 0.1))\n",
    "                for gene, value in self.genes.items()\n",
    "            }\n",
    "            print(\"new genes: \", new_genes)\n",
    "            return SimpleLife(new_energy, new_genes, self.x, self.y)\n",
    "        else:\n",
    "            return None\n",
    "\n",
    "    def is_alive(self):\n",
    "        return self.energy > 0\n",
    "\n",
    "\n",
    "# 创建环境\n",
    "environment = Environment(window_width, window_height)\n",
    "\n",
    "# 创建生命体\n",
    "life = SimpleLife(\n",
    "    100, x=random.randint(0, window_width), y=random.randint(0, window_height)\n",
    ")\n",
    "# 创建一个时钟对象\n",
    "clock = pygame.time.Clock()\n",
    "# 设置背景颜色\n",
    "background_color = (255, 255, 255)  # 白色背景\n",
    "\n",
    "# 游戏主循环\n",
    "running = True\n",
    "while running:\n",
    "    for event in pygame.event.get():\n",
    "        if event.type == pygame.QUIT:\n",
    "            running = False\n",
    "\n",
    "    # 更新生命体的状态\n",
    "    try:\n",
    "        life.live(environment)\n",
    "    except Exception as e:\n",
    "        print(\"error occurred, \", e)\n",
    "        pygame.quit()\n",
    "        e.with_traceback()\n",
    "\n",
    "    # 清空当前画布\n",
    "    window.fill(background_color)\n",
    "\n",
    "    # 绘制生命体\n",
    "    print(\"draw life: \", (life.x, life.y))\n",
    "    pygame.draw.circle(window, blue, (life.x, life.y), 10)\n",
    "\n",
    "    # 绘制食物\n",
    "    for food in environment.food_sources:\n",
    "        pygame.draw.circle(window, green, (food[\"x\"], food[\"y\"]), 5)\n",
    "\n",
    "    # 绘制危险\n",
    "    for danger in environment.dangers:\n",
    "        pygame.draw.circle(window, red, (danger[\"x\"], danger[\"y\"]), 5)\n",
    "\n",
    "    # 更新屏幕\n",
    "    pygame.display.flip()\n",
    "\n",
    "    # 限制帧率为1 FPS\n",
    "    clock.tick(1)\n",
    "\n",
    "\n",
    "# 退出游戏|\n",
    "pygame.quit()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
